﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using APIKeysLib;

namespace NetLib
{
    public static class FatSecretFetcher
    {
        #region Data and Properties

        public const string URL_BASE = "http://platform.fatsecret.com/rest/server.api?";
        public const string RequestTokenURL = "http://www.fatsecret.com/oauth/request_token?";
        private static OAuthBase Auth = new OAuthBase();

        #endregion //Data and Properties

        #region Methods

        [Pure]
        public static string SignedRequest(string parameters, string token = "", string tokenSecret = "")
        {
            string normalizedURL;
            string normalizedRequestParameters;

            string signature = Auth.GenerateSignature(new Uri(URL_BASE + parameters), FatSecretAPIKeys.ConsumerKey,
                FatSecretAPIKeys.ConsumerSecret, token, tokenSecret, "GET", Auth.GenerateTimeStamp(),
                Auth.GenerateNonce(), out normalizedURL, out normalizedRequestParameters);

            Debug.WriteLine(signature);
            Debug.WriteLine(normalizedURL);
            Debug.WriteLine(normalizedRequestParameters);




            //string results = RESTProcessor.HttpPost(normalizedURL, normalizedRequestParameters + "&oauth_signature=" + signature);
            string results = RESTProcessor.HttpGet(normalizedURL + "?" + normalizedRequestParameters 
                + "&oauth_signature=" + signature);

            return results;

        }

        [Pure]
        public static string SignedAndDelegatedRequest(string parameters, string userSecret = "")
        {
            string normalizedURL;
            string normalizedRequestParameters;

            //"format=json&method=food.get&food_id=33691&"
            string token = "";
            string tokenSecret = "";

            string signature = Auth.GenerateSignature(new Uri(URL_BASE + parameters), FatSecretAPIKeys.ConsumerKey,
                FatSecretAPIKeys.ConsumerSecret + "&" + userSecret, token, tokenSecret, "GET", Auth.GenerateTimeStamp(),
                Auth.GenerateNonce(), out normalizedURL, out normalizedRequestParameters);

            Debug.WriteLine(signature);
            Debug.WriteLine(normalizedURL);
            Debug.WriteLine(normalizedRequestParameters);




            //string results = RESTProcessor.HttpPost(normalizedURL, normalizedRequestParameters + "&oauth_signature=" + signature);
            string results = RESTProcessor.HttpGet(normalizedURL + "?" + normalizedRequestParameters + "&oauth_signature=" + signature);

            return results;

        }

        [Pure]
        public static string Food_Get(int foodID, bool returnJson = true)
        {
            //food_id=33691&format=json&method=food.get&
            string parameters = string.Format("food_id={0}&method=food.get&", foodID.ToString());
            if (returnJson)
            {
                parameters += "format=json&";
            }

            return SignedRequest(parameters);

        }

        public static string[] NewUserRequestToken()
        {
            string[] results = new string[3];
            int numRetries = 5;

            string oauth_token = "";
            string oauth_token_secret = "";
            string[] split;

            do
            {
                string result = GetRequestToken();

                if (result.Contains("oauth_callback_confirmed") && result.Contains("true"))
                {
                    split = result.Split('&');
                    foreach (var arg in split)
                    {
                        var splitarg = arg.Split('=');
                        if (splitarg[0] == "oauth_token")
                        {
                            results[1] = oauth_token = splitarg[1];
                        }
                        if (splitarg[0] == "oauth_token_secret")
                        {
                            results[2] = oauth_token_secret = splitarg[1];
                        }
                    }
                    results[0] = string.Format("http://www.fatsecret.com/oauth/authorize?oauth_token={0}", oauth_token);
                }
                else
                {
                    results[0] = "Failed";
                }

                numRetries--;
            } while (results[0] == "Failed" && numRetries <= 0);

            return results;
        }

        [Pure]
        private static string GetRequestToken()
        {
            
            string parameters = "&oauth_callback=oob&";

            string token = "";
            string tokenSecret = "";
            
            string normalizedURL;
            string normalizedRequestParameters;

            //"format=json&method=food.get&food_id=33691&"
            token = "";
            tokenSecret = "";

            string signature = Auth.GenerateSignature(new Uri(RequestTokenURL + parameters), FatSecretAPIKeys.ConsumerKey,
                FatSecretAPIKeys.ConsumerSecret, token, tokenSecret, "GET", Auth.GenerateTimeStamp(),
                Auth.GenerateNonce(), out normalizedURL, out normalizedRequestParameters);
            Debug.WriteLine(signature);
            Debug.WriteLine(normalizedURL);
            Debug.WriteLine(normalizedRequestParameters);

            Debug.WriteLine(normalizedRequestParameters);

            //string results = RESTProcessor.HttpPost(normalizedURL, normalizedRequestParameters + "&oauth_signature=" + signature);
            string results = RESTProcessor.HttpGet(normalizedURL + "?" + normalizedRequestParameters + "&oauth_signature=" + signature);

            Debug.WriteLine("Authorization info" + results);



            return results;
        }

        [Pure]
        public static string NewUserAccessToken(string request_token, string request_key, string oauth_verifier)
        {
            string AuthorizeURL = "http://www.fatsecret.com/oauth/access_token";


            string parameters = "?oauth_verifier=" + oauth_verifier + "&";
            
            string normalizedURL;
            string normalizedRequestParameters;

            //"format=json&method=food.get&food_id=33691&"
            //token = "";
            //tokenSecret = "";

            string signature = Auth.GenerateSignature(new Uri(AuthorizeURL + parameters), FatSecretAPIKeys.ConsumerKey,
                FatSecretAPIKeys.ConsumerSecret, request_token, request_key, "GET", Auth.GenerateTimeStamp(),
                Auth.GenerateNonce(), out normalizedURL, out normalizedRequestParameters);

            Debug.WriteLine(signature);
            Debug.WriteLine(normalizedURL);
            Debug.WriteLine(normalizedRequestParameters);

            //string results = RESTProcessor.HttpPost(normalizedURL, "?" + normalizedRequestParameters + "&oauth_signature=" + signature);

            string temp = normalizedURL + "?" + normalizedRequestParameters + "&oauth_signature=" + signature;
            string results = RESTProcessor.HttpGet(temp);

            return results;



        }

        [Pure]
        public static string ProfileCreate(bool returnJson = true)
        {

            string parameters = "method=profile.create&";
            if (returnJson)
            {
                parameters += "format=json&";
            }

            return SignedRequest(parameters);
        }

        [Pure]
        public static string ProfileGet(string auth_token, string auth_secret, bool returnJson = true)
        {
            string parameters = string.Format("method=profile.get&");
            if (returnJson)
            {
                parameters += "format=json&";
            }

            //return SignedAndDelegatedRequest(parameters, auth_secret);
            return SignedRequest(parameters, auth_token, auth_secret);
        }

        [Pure]
        public static List<FatSecretFoodEntriesResults> FoodEntriesGetMonth(string auth_token, string auth_secret,
            bool returnJson = true)
        {
            return FoodEntriesGetMonth(auth_token, auth_secret, "", returnJson);
        }
        [Pure]
        public static List<FatSecretFoodEntriesResults> FoodEntriesGetMonth(string auth_token, string auth_secret,
            string date, bool returnJson = true)
        {
            List<FatSecretFoodEntriesResults> returnValues = new List<FatSecretFoodEntriesResults>();

            string parameters = string.Format("method=food_entries.get_month&");
            if (!string.IsNullOrEmpty(date))
            {
                parameters += string.Format("date={0}&", date);
            }
            if (returnJson)
            {
                parameters += "format=json&";
            }

            do
            {
                //return SignedAndDelegatedRequest(parameters, auth_secret);
                string results = SignedRequest(parameters, auth_token, auth_secret);

                try
                {
                    FatSecretFoodEntriesGetMonthResults monthResults =
                       JsonConvert.DeserializeObject<FatSecretFoodEntriesGetMonthResults>(results);
                    if (monthResults.month != null)
                    {
                        returnValues
                            .AddRange(monthResults.month.day
                            .OrderByDescending(sort => sort.date_int)
                            .Select(x => x.ToFinalResultFormat()));
                    }
                }
                catch (Exception)
                {
                    try
                    {
                        FatSecretFoodEntriesGetMonthResultsOneDayReturned monthResults =
                        JsonConvert.DeserializeObject<FatSecretFoodEntriesGetMonthResultsOneDayReturned>(results);
                        if (monthResults.month != null)
                        {
                            returnValues
                                .Add(monthResults.month.day.ToFinalResultFormat());
                        }
                    }
                    catch (NullReferenceException)
                    {
                        //Month results were empty
                        return returnValues;
                    }
                }

                Debug.WriteLine("Asking for Food Results");
            } while (returnValues.Count == 0);

            return returnValues;
        }

        [Pure]
        public static List<FatSecretExerciseEntriesResults> ExerciseEntriesGetMonth(string auth_token, string auth_secret, bool returnJson = true)
        {
            return ExerciseEntriesGetMonth(auth_token, auth_secret, "", returnJson);
        }
        [Pure]
        public static List<FatSecretExerciseEntriesResults> ExerciseEntriesGetMonth(string auth_token, string auth_secret, string date,
            bool returnJson = true)
        {
            List<FatSecretExerciseEntriesResults> returnValues = new List<FatSecretExerciseEntriesResults>();


            string parameters = string.Format("method=exercise_entries.get_month&");
            if (!string.IsNullOrEmpty(date))
            {
                parameters += string.Format("date={0}&", date);
            }
            if (returnJson)
            {
                parameters += "format=json&";
            }

            do
            {
                //return SignedAndDelegatedRequest(parameters, auth_secret);
                string results = SignedRequest(parameters, auth_token, auth_secret);


                try
                {
                    FatSecretExerciseEntriesGetMonthResults monthResults =
                        JsonConvert.DeserializeObject<FatSecretExerciseEntriesGetMonthResults>(results);

                    if (monthResults.month != null)
                    {

                        returnValues
                            .AddRange(monthResults.month.day
                            .OrderByDescending(sort => sort.date_int)
                            .Select(x => x.ToFinalResultFormat()));
                    }
                }
                catch
                {
                    try
                    {
                        FatSecretExerciseEntriesGetMonthResultsOneDayReturned monthResults =
                                        JsonConvert.DeserializeObject<FatSecretExerciseEntriesGetMonthResultsOneDayReturned>(results);
                        if (monthResults.month != null)
                        {

                            returnValues
                                .Add(monthResults.month.day.ToFinalResultFormat());
                        }
                    }
                    catch (NullReferenceException)
                    {
                        //Month results were empty
                        return returnValues;
                    }
                }

                
                Debug.WriteLine("Asking for Exercise Results");
            } while (returnValues.Count == 0);


            return returnValues;
        }



        [Pure]
        public static List<FatSecretWeightResults> WeightGetMonth(string auth_token, string auth_secret, bool returnJson = true)
        {
            return WeightGetMonth(auth_token, auth_secret, "", returnJson);
        }
        [Pure]
        public static List<FatSecretWeightResults> WeightGetMonth(string auth_token, string auth_secret, string date,
            bool returnJson = true)
        {
            List<FatSecretWeightResults> returnValues = new List<FatSecretWeightResults>();


            string parameters = string.Format("method=weights.get_month&");
            if (!string.IsNullOrEmpty(date))
            {
                parameters += string.Format("date={0}&", date);
            }
            if (returnJson)
            {
                parameters += "format=json&";
            }

            do
            {
                //return SignedAndDelegatedRequest(parameters, auth_secret);
                string results = SignedRequest(parameters, auth_token, auth_secret);

                try
                {
                    FatSecretWeightGetMonthResults monthResults =
                    JsonConvert.DeserializeObject<FatSecretWeightGetMonthResults>(results);
                    if (monthResults.month != null)
                    {

                        returnValues
                            .AddRange(monthResults.month.day
                            .OrderByDescending(sort => sort.date_int)
                            .Select(x => x.ToFinalResultFormat()));
                    }
                }
                catch (Exception)
                {

                    try
                    {
                        FatSecretWeightGetMonthResultsOneDay monthResults =
                                    JsonConvert.DeserializeObject<FatSecretWeightGetMonthResultsOneDay>(results);
                        if (monthResults.month != null)
                        {
                            returnValues
                                .Add(monthResults.month.day.ToFinalResultFormat());
                        }
                    }
                    catch (NullReferenceException)
                    {
                        //Month results were empty
                        return returnValues;
                    }
                }


                
            } while (returnValues.Count == 0);


            return returnValues;
        }


        #endregion //Methods

    }

    #region FatSecret Data classes

    #region Food Data Holders
    internal class FatSecretFoodEntriesGetMonthResults
    {
        public FatSecretFoodEntriesGetMonthResultsMonth month;
    }
    internal class FatSecretFoodEntriesGetMonthResultsMonth
    {
        public FatSecretFoodEntriesGetMonthResultsDay[] day;
        public int from_date_int;
        public int to_date_int;
    }

    internal class FatSecretFoodEntriesGetMonthResultsOneDayReturned
    {
        public FatSecretFoodEntriesGetMonthResultsMonthOneDayReturned month;
    }
    internal class FatSecretFoodEntriesGetMonthResultsMonthOneDayReturned
    {
        public FatSecretFoodEntriesGetMonthResultsDay day;
        public int from_date_int;
        public int to_date_int;
    }
    internal class FatSecretFoodEntriesGetMonthResultsDay
    {
        public int calories;
        public double carbohydrate;
        public int date_int;
        public double fat;
        public double protein;

        #region methods
        public FatSecretFoodEntriesResults ToFinalResultFormat()
        {
            return new FatSecretFoodEntriesResults(date_int, calories, carbohydrate, fat, protein);
        }

        #endregion //methods
    }

    public class FatSecretFoodEntriesResults : IFoodIntakeResults
    {
        #region Data and Properties

        private DateTime _Date;
        public DateTime Date
        {
            get
            {
                return _Date;
            }

        }

        private double _Calories;
        public double CaloriesEaten
        {
            get
            {
                return _Calories;
            }
            private set
            {
                _Calories = value;
            }
        }

        private double _Carbohydrates;
        public double Carbohydrates
        {
            get
            {
                return _Carbohydrates;
            }
            private set
            {
                _Carbohydrates = value;
            }
        }

        private double _Fat;
        public double Fat
        {
            get
            {
                return _Fat;
            }
            private set
            {
                _Fat = value;
            }
        }

        private double _Protein;
        public double Protein
        {
            get
            {
                return _Protein;
            }
            private set
            {
                _Protein = value;
            }
        }

        #endregion //Data and Properties

        #region Constructors

        public FatSecretFoodEntriesResults()
        {
        }
        public FatSecretFoodEntriesResults(int date_int, int calories, double carbs, double fat, double protein)
        {
            _Date = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddDays(date_int);
            CaloriesEaten = calories;
            Carbohydrates = carbs;
            Fat = fat;
            Protein = protein;
        }

        #endregion //Constructors

        #region Methods

        public override string ToString()
        {
            return string.Format("Date: {1}, CaloriesBurned: {0}", CaloriesEaten, Date);
        }

        #endregion //Methods

    }

    #endregion Food Data Holders

    #region Exercise Data Holders
    internal class FatSecretExerciseEntriesGetMonthResults
    {
        public FatSecretExerciseEntriesGetMonthResultsMonth month;
    }
    internal class FatSecretExerciseEntriesGetMonthResultsMonth
    {
        public FatSecretExerciseEntriesGetMonthResultsDay[] day;
        public int from_date_int;
        public int to_date_int;
    }
    internal class FatSecretExerciseEntriesGetMonthResultsOneDayReturned
    {
        public FatSecretExerciseEntriesGetMonthResultsMonthOneDayReturned month;
    }
    internal class FatSecretExerciseEntriesGetMonthResultsMonthOneDayReturned
    {
        public FatSecretExerciseEntriesGetMonthResultsDay day;
        public int from_date_int;
        public int to_date_int;
    }
    internal class FatSecretExerciseEntriesGetMonthResultsDay
    {
        public int calories;
        public int date_int;

        #region methods
        public FatSecretExerciseEntriesResults ToFinalResultFormat()
        {
            return new FatSecretExerciseEntriesResults(date_int, calories);
        }

        #endregion //methods
    }

    public class FatSecretExerciseEntriesResults : IExerciseResults
    {
        #region Data and Properties

        private DateTime _Date;
        public DateTime Date
        {
            get
            {
                return _Date;
            }

        }

        private double _Calories;
        public double CaloriesBurned
        {
            get
            {
                return _Calories;
            }
            private set
            {
                _Calories = value;
            }
        }

        #endregion //Data and Properties

        #region Constructors

        public FatSecretExerciseEntriesResults()
        {
        }
        public FatSecretExerciseEntriesResults(int date_int, int calories)
        {
            _Date = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddDays(date_int);
            CaloriesBurned = calories;
        }

        #endregion //Constructors

        #region Methods

        public override string ToString()
        {
            return string.Format("Date: {1}, CaloriesBurned Burned: {0}", CaloriesBurned, Date);
        }
        #endregion //Methods







    }
    #endregion //Exercise Data Holders

    #region Weight Holders
    internal class FatSecretWeightGetMonthResults
    {
        public FatSecretWeightGetMonthResultsMonth month;
    }
    internal class FatSecretWeightGetMonthResultsMonth
    {
        public FatSecretWeightGetMonthResultsDay[] day;
        public int from_date_int;
        public int to_date_int;
    }
    internal class FatSecretWeightGetMonthResultsOneDay
    {
        public FatSecretWeightGetMonthResultsMonthOneDay month;
    }
    internal class FatSecretWeightGetMonthResultsMonthOneDay
    {
        public FatSecretWeightGetMonthResultsDay day;
        public int from_date_int;
        public int to_date_int;
    }
    internal class FatSecretWeightGetMonthResultsDay
    {
        public int date_int;
        public string weight_comment;
        public double weight_kg;

        #region methods
        public FatSecretWeightResults ToFinalResultFormat()
        {
            return new FatSecretWeightResults(date_int, weight_kg);
        }

        #endregion //methods
    }

    public class FatSecretWeightResults : IWeightResults
    {
        #region Data and Properties

        private DateTime _Date;
        public DateTime Date
        {
            get
            {
                return _Date;
            }
            private set
            {
                _Date = value;
            }

        }

        private double _Weight;
        public double Weight
        {
            get
            {
                return _Weight;
            }
            private set
            {
                _Weight = value;
            }
        }

        #endregion //Data and Properties

        #region Constructors

        public FatSecretWeightResults()
        {
        }
        public FatSecretWeightResults(int date_int, double weight_kg)
        {
            _Date = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddDays(date_int);
            Weight = weight_kg * 2.20462262;
        }

        #endregion //Constructors

        #region Methods

        public override string ToString()
        {
            return string.Format("Date: {1}, Weight: {0}", Weight, Date);
        }

        #endregion //Methods

    }

    #endregion Food Data Holders


    #endregion //FatSecret Data classes

}
